查看原文
其他

FlutterComponent最佳实践之色彩管理

徐宜生 群英传 2022-12-21

点击上方蓝字关注我,知识会给你力量


Flutter中关于色彩和主题的内容非常之多,我们需要理清不同的Color之间的异同,才能更好的开发Flutter应用。

MaterialColor

在ThemeData的构造函数中,我们可以发现两个很有意思的属性

MaterialColor? primarySwatch,
Color? primaryColor,

在Flutter创建的Demo中,Theme是这样设置的。

return MaterialApp(
  title: 'Flutter Demo',
  theme: ThemeData(
    primarySwatch: Colors.blue,
  ),
  home: const MyHomePage(title: 'Flutter Demo Home Page'),
);

有没有人很好奇,primarySwatch和primaryColor,到底要设置哪个?到底谁才是真正的「主色调」?

首先,MaterialColor并不等于Color,它是基于MaterialDesign而产生的一套颜色体系。

material_design_color_system

在这个颜色系统中,基色和明暗不同的10种颜色作为一组处理,从而形成了MaterialColor。

前面代码中的Colors.blue,实际上就是一个MaterialColor,我们来看下它的实现。

static const MaterialColor blue = MaterialColor(
  _bluePrimaryValue,
  <int, Color>{
     50: Color(0xFFE3F2FD),
    100: Color(0xFFBBDEFB),
    200: Color(0xFF90CAF9),
    300: Color(0xFF64B5F6),
    400: Color(0xFF42A5F5),
    500: Color(_bluePrimaryValue),
    600: Color(0xFF1E88E5),
    700: Color(0xFF1976D2),
    800: Color(0xFF1565C0),
    900: Color(0xFF0D47A1),
  },
);
static const int _bluePrimaryValue = 0xFF2196F3;

由此可见,MaterialColor的10种颜色是怎么实现的。

事实上,MaterialColor的定义就是如此,一个基色,加上一个不同shade的Map。

image-20220213131410151

如果你要自定义一个MaterialColor,那么这10种色调,也是必须要实现的。

除了MaterialColor以外,还有一个MaterialAccentColor,它和MaterialColor类似,但是只有5种色调。

Color

Colors:这个类是来自Material调色板的颜色。要在代码中访问它们,只需调用基色和shade值即可。

color: Colors.red
color: Colors.red[200]

Color:你可以将这个类用于Material调色板以外的颜色,因为它允许ARGB(Alpha, Red, Green, Blue)格式的颜色值。最常见的使用方法是像下面的代码这样传递十六进制颜色代码,其中0xFF代表完全不透明的颜色。

Color(0xFF42A5F5)

primarySwatch

接下来,我们继续来看前面提到的那个问题,为什么ThemeData中需要设置primarySwatch。

在theme_data的源代码中,我们可以发现这样的代码。

primarySwatch ??= Colors.blue;
primaryColor ??= isDark ? Colors.grey[900]! : primarySwatch;
final Brightness _primaryColorBrightness = estimateBrightnessForColor(primaryColor);
primaryColorLight ??= isDark ? Colors.grey[500]! : primarySwatch[100]!;
primaryColorDark ??= isDark ? Colors.black : primarySwatch[700]!;

从这里,我们就可以知道为什么在Demo中设不设置primarySwatch都会是蓝色的主题色的原因了。

那么一个具体的Flutter组件,是如何决定自己的主题的呢?以Appbar为例,我们在源代码中找到对应设置background的地方。

final Color backgroundColor = backwardsCompatibility
  ? widget.backgroundColor
    ?? appBarTheme.backgroundColor
    ?? theme.primaryColor
  : _resolveColor(
      states,
      widget.backgroundColor,
      appBarTheme.backgroundColor,
      colorScheme.brightness == Brightness.dark ? colorScheme.surface : colorScheme.primary,
    );

不要被这里茫茫多的问号搞昏了,复习一下Dart语法吧。

  • 「?.」——代表非空访问,例如「myObject?.someProperty」,等价于——「(myObject != null) ? myObject.someProperty : null」
  • 「??」——代表避空判断,例如「a ?? 3」a为空时,返回3
  • 「??=」——同样是避空赋值,例如「a ??= 3」a为空时,a赋值为3

了解了这些之后,你应该就能看懂上面的代码了,原来Appbar的background是经过很多场景来判断的,简而言之:

  • 先判断是否在Appbar中设置了backgroundColor
  • 再判断是否指定了AppBarTheme.backgroundColor,也就是针对Appbar进行的Theme覆盖
  • 最后再根据是否黑夜模式来判断使用ColorScheme.primary还是ColorScheme.surface

大部分的Flutter组件,几乎都遵循这个判断流程,只是使用的Color类型不太一样。

但是,primaryColor并不是没用了,它可以用来更改组件的Theme,用于局部主题的使用。

Expanded(
    child: Theme(
        data: Theme.of(context).copyWith(primaryColor: Colors.red),
        child: Container(
          padding: const EdgeInsets.all(15.0),
          color: Theme.of(context).primaryColor,
          child: Text(
            'This Container overrides primaryColor',
            style: Theme.of(context).headline5,
          ),
        )))

ColorScheme

色彩的千变万化,最终会导致Theme属性的膨胀,这是可以预见的,所以你可以看看ThemeData有多少属性需要配置就知道了。

在ThemeData的构造函数中,有超过70种的Color和Theme,这要全部通过手工来配置,将是一个非常大的工作量。

因此,Flutter引入了ColorScheme属性,它是一组基于Material规范的25种颜色(9种必选色),可用于配置大多数组件的颜色属性。Flutter团队计划用定义好的ColorScheme来设计材质组件的样式。要使用colorScheme,你必须调用ThemeData.from()构造函数。

ThemeData.from(
  colorScheme: const ColorScheme.light().copyWith(
    primary: const Color(0xff455a64),
    primaryContainer: const Color(0xff1c313a),
    secondary: const Color(0xffffc400),
    secondaryContainer: const Color(0xffc79400),
  ),
);

创建ColorScheme只需要给对应的属性填上不同的色值即可。

ColorScheme colorScheme = ColorScheme(
  brightness: isDark ? Brightness.dark : Brightness.light,
  primary: accent1,
  onPrimary: Colors.white,
  secondary: accent1,
  onSecondary: Colors.white,
  error: Colors.red.shade400,
  onError: Colors.red.shade400,
  background: bg1,
  onBackground: textColor,
  surface: bg1,
  onSurface: textColor,
);

新版本的Flutter,还提供了fromSeed方法,让开发者可以根据一个基色来生成符合Material Design规范的ColorScheme。

MaterialDesign提供了ThemeBuilder来帮助开发者创建这些代码。

https://material-foundation.github.io/material-theme-builder/#/custom

下面这张图,就展示了Flutter中不同的Color之间的关系。

img

向大家推荐下我的网站 https://xuyisheng.top/  点击原文一键直达

专注 Android-Kotlin-Flutter 欢迎大家访问



往期推荐


本文原创公众号:群英传,授权转载请联系微信(Tomcat_xu),授权后,请在原创发表24小时后转载。
< END >
作者:徐宜生

更文不易,点个“三连”支持一下👇


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存